<?php
namespace lib;

class View{

    /**
     * @var array
     */
    private $data = array();

    /**
     * @var null
     */
    private $conf;

    function __construct(){
        $this->conf = Conf::get('template');
    }

    /**
     * 模版赋值
     * @param $key
     * @param $val
     * @return mixed
     */
    public function set($key, $val){
        return $this->data[$key] = $val;
    }

    /**
     * 输出模版
     * @param string $filename
     * @throws \Exception
     */
    public function display($filename = ''){
        $filename = $this->positionTemp($filename);
        if($this->conf['open_cache']){     //如果开启了缓存
            $cacheFile = Cache::filename($filename);
            if(file_exists($cacheFile)){
                include $cacheFile;
            }else{
                echo $this->fetch($filename);
            }
        }else{      //解析模板
            echo $this->fetch($filename);
        }
    }

    /**
     * 解析模版
     * @param $filename
     * @return string
     * @throws \Exception
     */
    public function fetch($filename = ''){
        $filename = $this->positionTemp($filename);
        if(!is_file($filename)){ throw new \Exception("模版文件{$filename}不存在!"); }
        if($this->conf['open_cache']) {     //如果开启了缓存
            $cacheFile = Cache::filename($filename);
            if(file_exists($cacheFile)){
                return file_get_contents($cacheFile);
            }
        }
        $temp = file_get_contents($filename);
        if($temp){
            return $this->compiler($filename, $temp);      //编译模板
        }else{
            return '';
        }
    }

    /**
     * 编译保存模板
     * @param $filename
     * @param $temp
     * @return bool|string
     */
    private function compiler($filename, $temp){
        $this->parse($temp);    //解析标签
        $tempPath = Cache::save($filename, $temp, ['path'=>TEMP_PATH, 'suffix' => 'php']);
        if($tempPath){     //如果模板保存成功
            extract($this->data, EXTR_OVERWRITE);
            //获取输出缓冲
            ob_start();
            ob_implicit_flush(0);
            include $tempPath;     //将缓存文件包含进当前缓冲区
            $contents = ob_get_clean(); //获取缓冲区内容
            if($this->conf['open_cache']){        //生成缓存html文件
                Cache::save($filename, $contents, ['expire'=>$this->conf['cache_time']]);
            }
            return $contents;
        }
        return '';
    }

    /**
     * 解析模板标签
     * @param $contents
     */
    private function parse(&$contents){
        if(empty($contents)){
            return;
        }
        //解析include标签
        $this->parseInclude($contents);
        //解析each标签
        $this->parseEach($contents);
        //解析 if elseif else标签
        $this->parseIf($contents);
        //解析模版标签
        $this->parseTag($contents);
    }

    /**
     * 解析Include标签
     * @param $contents
     */
    private function parseInclude(&$contents){
        $function = function() use(&$contents, &$function) {
            $pattern = '/\{\s*include\s*file=["|\'](.*?)["|\'].*?\}/is';
            if (preg_match_all($pattern, $contents, $matches, PREG_SET_ORDER)) {
                foreach ($matches as $val) {
                    $filename = str_replace(['\'', '"', '/'], ['', '', DS], VIEW_PATH . $val[1] . TEMP_EXT);
                    if(!is_file($filename)){ throw new \Exception("引入的模版{$val[1]}不存在!"); }
                    $joinContent = file_get_contents($filename);
                    $contents = str_replace($val[0], $joinContent, $contents);
                }
                $function($contents);
            }
        };
        $function($contents);
    }

    /**
     * 解析Foreach标签
     * @param $contents
     */
    private function parseEach(&$contents){
        $pattern = '/\{each\s*name=["|\'](.*?)["|\']\s*item=["|\'](.*?)["|\']\s*(key=["|\'](.*?)["|\'])?\}/is';
        preg_replace_callback($pattern, function($matches) use (&$contents){
            if(stripos($matches[1], '.')){
                $cache = explode('.', $matches[1]);
                $matches[1] = is_numeric($cache[1]) ? "{$cache[0]}[{$cache[1]}]" : "{$cache[0]}['{$cache[1]}']";
            }
            if(isset($matches[4])){
                $contents = str_replace($matches[0], "<?php foreach(\${$matches[1]} as \${$matches[4]} => \${$matches[2]}): ?>", $contents);
            }else {
                $contents = str_replace($matches[0], "<?php foreach(\${$matches[1]} as \${$matches[2]}): ?>", $contents);
            }
            $contents = str_replace('{/each}', '<?php endforeach; ?>', $contents);
        }, $contents);
    }

    /**
     * 解析If标签
     * @param $contents
     */
    private function parseIf(&$contents){
        preg_replace_callback('/\{if\s*\(([^\n\r]*)\).*?\}/is', function($matches) use (&$contents) {
            $contents = str_replace($matches[0], "<?php if({$matches[1]}): ?>", $contents);
            preg_replace_callback('/\{elseif\s*\(([^\n\r]*?)\).*?\}/is', function($matches) use (&$contents){
                $condition = str_replace(' ', '', $matches[1]);
                $contents = str_replace($matches[0], "<?php elseif({$condition}): ?>", $contents);
            }, $contents);
            $contents = str_replace('{else}', "<?php else: ?>", $contents);
            $contents = str_replace('{/if}', "<?php endif; ?>", $contents);
        }, $contents);
    }

    /**
     * 解析模版标签
     * @param $contents
     */
    private function parseTag(&$contents){
        $pattern = '/\{((:|\$).*?)\}/';
        preg_replace_callback($pattern, function($matches) use (&$contents){
            if($matches[2] == ':'){     //调用方法
                $filter = preg_replace("/^{$matches[2]}(.*?)$/", '$1', $matches[1]);
                $contents = str_replace($matches[0], "<?php echo {$filter} ?>", $contents);
            }else{      //调用变量
                if(stripos($matches[1], '.')){
                    $cache = explode('.', $matches[1]);
                    $temp = is_numeric($cache[1]) ? "{$cache[0]}[{$cache[1]}]" : "{$cache[0]}['{$cache[1]}']";
                    $contents = str_replace($matches[0], "<?php echo {$temp} ?>", $contents);
                }else{
                    $contents = str_replace($matches[0], "<?php echo {$matches[1]} ?>", $contents);
                }
            }
        }, $contents);
    }

    /**
     * 解析模版调用目录
     * @param $path
     * @return string
     */
    private function positionTemp($path){
        if(empty($path)){
            $filename = VIEW_PATH . CONTROLLER_NAME . DS .  ACTION_NAME . TEMP_EXT;
        }else{
            if(is_file($path)){ return $path; }
            $pathArr = explode('/', $path);
            if(count($pathArr) == 2){
                $filename = VIEW_PATH . $pathArr[0] . DS .  $pathArr[1] . TEMP_EXT;
            }else if(count($pathArr) == 1){
                $filename = VIEW_PATH . CONTROLLER_NAME . DS .  $pathArr[0] . TEMP_EXT;
            }else{
                $filename = VIEW_PATH . CONTROLLER_NAME . DS .  ACTION_NAME . TEMP_EXT;
            }
        }
        return strtolower($filename);
    }

}